//
//  AudioManager.m
//  LineByLine
//
//  Created by 병철 김 on 11. 10. 21..
//  Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//

#import "AudioManager.h"


#define kMaxRecordSeconds 30
#define kMuteTimeLimit 15
#define kMutePowerLimit 15
#define kIsSpeakAverage -40
#define kIsSpeakCount 5

@implementation AudioManager
@synthesize audioPlayer, audioDelegate;

+ (AudioManager*) sharedAudioManager {
    static dispatch_once_t onceToken;
    static AudioManager *sharedInstance = nil;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    
    return sharedInstance;
}

- (id) init {
    self = [super init];
    if (self) {
        [self setUpAudioSession];
        avgPower = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)dealloc {
    [self closePlayer];
    [avgPower release];
    [super dealloc];
}

- (void) setUpAudioSession {
    NSError *error = nil;
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setDelegate:self];
    [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
    
    if (error) {
        NSLog(@"session error : %@", error);
    }
    
    UInt32 doChangeDefaultRoute = 1;
    AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
    [session setActive:YES error:&error];

    if (error) {
        NSLog(@"session activation error : %@", error);
    }
}

- (void) closePlayer {
    [self stopPlay];
    [self stopRecord];
    
    if (audioPlayer != nil) {
        [audioPlayer release];
        audioPlayer = nil;
    }
    
    if (audioRecorderPlayer != nil) {
        [audioRecorderPlayer release];
        audioRecorderPlayer = nil;
    }
}

- (BOOL) prepareBook:(id) _book {
    book = _book;
    NSString *media = [book valueForKey:@"media"];
    if (media == nil || [media length] <= 0) {
        NSLog(@"prepareBook Error : no media");
        return NO;
    }
    
    NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"/Library/Caches/%@", media]];
    NSURL *fileUrl = [[NSURL alloc] initFileURLWithPath:filePath];
    
    if (audioPlayer != nil) {
        [self closePlayer];
    }
    
    NSError *error = nil;
    audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
    [audioPlayer setVolume:1.0];
    [audioPlayer setNumberOfLoops:0];
    [audioPlayer prepareToPlay];
    
    if (error) {
        NSLog(@"prepareBook Error : %@", error);
        return NO;
    }
    
    return YES;
}

- (void) playBookStart:(float)start duration:(float)duration {
    [self stopPlay];
    [self stopRecord];
    
    [audioPlayer setCurrentTime:start];
    [audioPlayer play];
    
    timer = [[NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(stopPlay) userInfo:nil repeats:NO] retain];;
}

- (void) pausePlay {
    if (audioPlayer != nil && [audioPlayer isPlaying]) {
        [audioPlayer pause];
        [audioDelegate didFinishPlaying];
    }
    
    if (audioRecorderPlayer != nil && [audioRecorderPlayer isPlaying]) {
        [audioRecorderPlayer stop];
    }
    
    if (timer != nil && [timer isValid]) {
        [timer invalidate];
        [timer release];
    }
    timer = nil;

}

- (void) stopPlay {
    NSLog(@"stop play");
    if (audioPlayer != nil && [audioPlayer isPlaying]) {
        [audioPlayer stop];
        [audioDelegate didFinishPlaying];
    }
    
    if (audioRecorderPlayer != nil && [audioRecorderPlayer isPlaying]) {
        [audioRecorderPlayer stop];
    }
    
    if (timer != nil && [timer isValid]) {
        [timer invalidate];
        [timer release];
    }
    timer = nil;
}

- (void) beginInterruption {
    [self stopPlay];
}

#pragma mark - record
- (NSString*) recordFileUrl:(NSInteger) sentenceId {
	NSString *recordDirectory = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"record/%@/", [book valueForKey:@"bookId"]]];
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:recordDirectory]) {
        [fileManager createDirectoryAtPath:recordDirectory withIntermediateDirectories:YES attributes:nil error:nil];
    }
	
	NSString *fileName = [NSString stringWithFormat:@"%d.caf", sentenceId];
    NSString *filePathString = [recordDirectory stringByAppendingPathComponent:fileName];

    return filePathString;
}

- (NSDictionary*) recordSetting {
	NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init];
	[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
	[recordSetting setValue:[NSNumber numberWithFloat:12000.0] forKey:AVSampleRateKey];
	[recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];
    return [recordSetting autorelease];
}

- (void) recordBookPage:(NSInteger) sentenceId {
    [self stopPlay];
    [self stopRecord];

    NSString *filePathString = [self recordFileUrl:sentenceId];
    NSDictionary *recordSetting = [[self recordSetting] retain];

    if ([[NSFileManager defaultManager] fileExistsAtPath:filePathString]) {
        NSError *error = nil;
        [[NSFileManager defaultManager] removeItemAtPath:filePathString error:&error];
        if (error) {
            NSLog(@"%@", error);
        }
    }
    
    NSError *error = nil;
	audioRecorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:filePathString] settings:recordSetting error:&error];
	[recordSetting release];
    
    if (error) {
        NSLog(@"%@", error);
        //FIXME 여기서 에러 처리 
    }
    
    [audioRecorder setDelegate:self];
	[audioRecorder setMeteringEnabled:YES];
	BOOL result = [audioRecorder prepareToRecord];
    NSLog(@"record : %d", result);
	result = [audioRecorder recordForDuration:(NSTimeInterval) kMaxRecordSeconds];
    NSLog(@"record : %d", result);

    timer = [[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkRecording) userInfo:nil repeats:YES] retain];
    if (sentenceId > 0) {
        afterRecordProcess = @selector(afterBookRecord:);
    } else {
        afterRecordProcess = @selector(afterRetryRecord:);
    }
}

- (BOOL) playRecordBookPage:(NSInteger) sentenceId {
    [self stopPlay];
    [self stopRecord];
    
    NSString *filePathString = [self recordFileUrl:sentenceId];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:filePathString]) {
        return NO;
    }
    
    if (audioRecorderPlayer) {
        [audioRecorderPlayer release];
        audioRecorderPlayer = nil;
    }
        
    NSError *error;
    audioRecorderPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePathString] error:&error];
    
    if (error) {
        // FIXME Error handling here
        NSLog(@"audioRecorderPlayer error. %@", error);
        return NO;
    }
    
    [audioRecorderPlayer setVolume:1.0];
    [audioRecorderPlayer setNumberOfLoops:0];
    [audioRecorderPlayer prepareToPlay];
    [audioRecorderPlayer setDelegate:self];
    [audioRecorderPlayer play];
        
    return YES;
}

- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    if (player == audioRecorderPlayer) {
        [audioDelegate didFinishPlayRecord];
    }
}

- (void) stopRecord {
    NSLog(@"stop");
	[audioRecorder stop];
	[audioRecorder release];
	audioRecorder = nil;
    
    if (timer != nil && [timer isValid]) {
        [timer invalidate];
        [timer release];
    }
    timer = nil;
    [avgPower removeAllObjects];
    maxAverage = -160;
}

- (void) checkRecording {
//    이제 소리 검사 로직을 좀 바꿔 볼까?
    
    if ([audioRecorder isRecording]) {
        [audioRecorder updateMeters];
        float avg = [audioRecorder averagePowerForChannel:0];
        float peak = [audioRecorder peakPowerForChannel:0];
        float diff = peak - avg;
        [avgPower insertObject:[NSNumber numberWithFloat:avg] atIndex:0];
        
        NSLog(@"power avg : %f, peak : %f, diff : %f", avg, peak, diff);
        
        if ([avgPower count] < kMuteTimeLimit) {
            return;
        }
        
        while([avgPower count] > kMuteTimeLimit) {
            [avgPower removeObjectAtIndex:([avgPower count] - 1)];
        }
        
        float sum;
        for (int i=0; i<kMuteTimeLimit; i++) {
            sum += [[avgPower objectAtIndex:i] floatValue];
        }
        
        float average = sum / kMuteTimeLimit;
        if (average > maxAverage) {
            maxAverage = average;
        }
        
        [self performSelector:afterRecordProcess withObject:[NSNumber numberWithFloat:average]];
    }
}

- (SystemSoundID) soundFileObject {
    if (!soundFileObject) {
        CFBundleRef mainBundle = CFBundleGetMainBundle();
        CFURLRef beepSoundFile  = CFBundleCopyResourceURL (mainBundle, CFSTR ("tap"), CFSTR ("aif"), NULL);
        AudioServicesCreateSystemSoundID (beepSoundFile, &soundFileObject);
    }
    return soundFileObject;
}

- (void) playBeep {
    SystemSoundID _soundFileObject = [self soundFileObject];
    AudioServicesPlaySystemSound(_soundFileObject);
}

- (void) askRetry {
    [self recordBookPage:0];
}

- (void) afterBookRecord:(NSNumber*) average {
    if ((maxAverage - [average floatValue]) >  kMutePowerLimit) {
        NSLog(@"stop record : %f, %f", maxAverage, [average floatValue]);
        [self stopRecord];
        [audioDelegate didFinishRecording];
    }
}

- (void) afterRetryRecord:(NSNumber*) average {
    if ([avgPower count] >= kMuteTimeLimit) {
        [timer invalidate];
        [timer release];
        timer = nil;
    }
    
    int speakCount = 0;
    for (int i=0; i<kMuteTimeLimit; i++) {
        if ([[avgPower objectAtIndex:i] floatValue] >= kIsSpeakAverage) {
            speakCount++;
        }
    }
    
    [self stopRecord];
    
    if (speakCount >= kIsSpeakCount) {
        NSLog(@"retry : %d", speakCount);
        [audioDelegate didFinishRetry:YES];
    } else {
        NSLog(@"next : %d", speakCount);
        [audioDelegate didFinishRetry:NO];
    }
    
}

- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
    if (timer) {
        NSLog(@"time out record %d", flag);
        [self stopRecord];
        [self performSelector:afterRecordProcess withObject:[NSNumber numberWithInt:-200]];
    }
}

@end
